import pprint
import re
from datetime import datetime, timedelta
import sys

sys.path.append("../")  # noqa
from bson import ObjectId
from loguru import logger
from pymongo import MongoClient

from config import settings, col_institution, col_register, col_okr, col_top11, col_alumni, col_activity
from task_utils.util_mapping import identity_values

client = MongoClient(settings.dsn)

col_inst = col_institution
col_activity = col_activity
col_register = col_register
col_person = col_alumni
col_institution = col_institution

mapping_region = {
    "东部地区": ["北京市", "天津市", "河北省", "上海市", "江苏", "浙江省", "福建省", "山东省", "广东省", "海南省"],
    "中部地区": ["山西省", "安徽省", "江西省", "河南省", "湖北省", "湖南省"],
    "西部地区": ["内蒙古自治区", "广西壮族自治区", "重庆市", "四川省", "贵州省", "云南省", "西藏自治区", "陕西省",
                 "甘肃省", "青海省", "宁夏回族自治区", "新疆维吾尔自治区"],
    "东北地区": ["辽宁省", "吉林省", "黑龙江省"],
}


def init_province2region():
    province2region = {}
    for region, provinces in mapping_region.items():
        province2region.update({p: region for p in provinces})
    return province2region


province2region = init_province2region()


def get_quarter(dt):
    return (dt.month - 1) // 3 + 1


def query_year(start_year: int = None, end_year: int = None):
    if not start_year:
        start_year = datetime.utcnow().year
    if not end_year:
        end_year = start_year

    from_date = datetime(start_year, 1, 1) - timedelta(hours=8)
    to_date = datetime(end_year + 1, 1, 1) - timedelta(hours=8)
    year_range = {"$gte": from_date, "$lt": to_date}

    return year_range


def institution_query(inst_id):
    inst_id = ObjectId(inst_id)
    query = {"$or": [{"institution_id": inst_id}, {"co_inst_ids": {"$in": [inst_id]}}], "is_deleted": {"$ne": True}}
    return query


def international_tag(start_year: int = None, end_year: int = None, inst_id=None):
    """
    国际化程度评分： 根据活动标签中是否包含“多边”、“双边”、“港澳台”来取，如果包含这三个值的活动标签，都算国际化活动
    target = ["多边", "双边", "港澳台"]
    """

    query = {"end_time": query_year(start_year, end_year)}

    if inst_id:
        query.update(institution_query(inst_id))

    single_ids = col_activity.find({"tags": {"$regex": "多边"}, **query}).distinct("_id")

    double_ids = col_activity.find({"tags": {"$regex": "双边"}, **query}).distinct("_id")
    hk_ids = col_activity.find({"tags": {"$regex": "港澳台"}, **query}).distinct("_id")
    internation_ids = set(single_ids) | set(double_ids) | set(hk_ids)
    return len(single_ids), len(double_ids), len(hk_ids), len(internation_ids)


def act_importance_of_activities(start_year: int = None, end_year: int = None, inst_id=None):
    """
    {"常规": 1, "重点": 2, "重大": 3}
    """
    query = {"end_time": query_year(start_year, end_year)}
    if inst_id:
        query.update(institution_query(inst_id))

    regular, key, major, act_counts = 0, 0, 0, 0
    for activity in col_activity.find(query, projection=["importance_of_activities"]):
        act_counts += 1
        importance_of_activities = activity.get('importance_of_activities')
        importance_of_activities = importance_of_activities or '常规'  # value of importance_of_activities could be null, so must have this step
        if '常规' in importance_of_activities:
            regular += 1
        elif '重点' in importance_of_activities:
            key += 1
        elif '重大' in importance_of_activities:
            major += 1
        else:
            logger.warning(f"act_importance_of_activities::activity {str(activity['_id'])} "
                           f"has wrong importance_of_activities {importance_of_activities}")
    return regular, key, major, act_counts


def act_datetime(start_year: int = None, end_year: int = None, inst_id=None):
    """
    举办时间分布： 覆盖4个季度，12个月份。
    """
    query = {"end_time": query_year(start_year, end_year)}
    if inst_id:
        query.update(institution_query(inst_id))
    schedule = col_activity.find(query).distinct("end_time")

    months_set = {m.month for m in schedule}

    quarter_set = {get_quarter(dt) for dt in schedule}
    return len(months_set), len(quarter_set)


def act_region(start_year: int = None, end_year: int = None, inst_id=None):
    """
    举办地域分布： 辐射四大地区，30个省级行政区。
    """
    query = {"end_time": query_year(start_year, end_year)}
    if inst_id:
        query.update(institution_query(inst_id))
    # 去重 remove duplicates remove
    provinces = col_activity.find(query).distinct("region.province")
    regions = {province2region[p] for p in provinces if p in province2region}
    return len(regions), len(provinces)


# //》
#  活动联系人才 module


def person_overview(start_year: int = None, end_year: int = None, inst_id=None):
    """
    国家战略人才： 根据人才标签，分别取“战略科学家”、“领军人才”、“青年科技人才"、"卓越工程师”标签根据人才id去重数量
    """
    query = {"end_time": query_year(start_year, end_year), }
    if inst_id:
        query.update(institution_query(inst_id))
    # 活动数
    activity_ids = col_activity.find(query).distinct("_id")
    activity_len = len(activity_ids)
    person_db = col_register.find({"activity_id": {"$in": activity_ids}, "is_attended": True},
                                  {"person_id": 1, "_id": 0})
    contact_ids = [p.get("person_id") for p in person_db]

    person_db = col_person.find(
        {"_id": {"$in": contact_ids}, "is_deleted": {"$ne": True}},
        {"person_labels": 1, "job_province": 1, "professional_titles": 1}
    )
    person_docs = list(person_db)
    alumni_ids = [p.get("_id") for p in person_docs]
    person_len = len(person_docs)
    # 考虑有无效的报名id
    contact_len = len([c for c in contact_ids if c in alumni_ids])

    def strategic_talent():
        """
        地域分布: 人才job_province覆盖除“国外”以上的数量。
        海外人才参加活动
        """
        provinces = set()
        for item in person_docs:
            job_province = item.get("job_province")
            if not job_province or job_province == "国外":
                continue
            provinces.add(job_province)
        return len(provinces)

    def oversea_talent():
        """
        海外人才
        """
        oversea = [item for item in person_docs if item.get("job_province") == "国外"]
        return len(oversea)

    def count_title():
        """
        国家战略人才： 根据人才标签，分别取“战略科学家”、“领军人才”、“青年科技人才"、"卓越工程师”标签根据人才id去重数量
        """
        target = ["战略科学家", "领军人才", "青年科技人才", "卓越工程师"]
        strategic_scientists = []
        leading_talents = []
        young = []
        outstanding_engineers = []
        for doc in person_docs:
            tags = doc.get("person_labels") or []  # value of person_labels could be null
            if "战略科学家" in tags:
                strategic_scientists.append(1)
            if "领军人才" in tags:
                leading_talents.append(1)
            if "青年科技人才" in tags:
                young.append(1)
            if "卓越工程师" in tags:
                outstanding_engineers.append(1)
        return len(strategic_scientists), len(leading_talents), len(young), len(outstanding_engineers)

    def count_professional_titles():
        positions = {"教授", "研究员", "主任医师", "编审", "高级记者", "高级编辑", "一级律师", "研究馆员", "院士",
                     "正高级经济师", "正高级工程师", "正高级教师"}
        matched_people = []
        for doc in person_docs:
            _titles = doc.get("professional_titles", [])
            if not _titles:
                continue
            matched_titles = set(_titles) & positions
            if matched_titles:
                matched_people.append(str(doc['_id']))
        return len(matched_people)

    def _inst_okr():
        """年初预期okr"""

        okr_predict = col_okr.find_one({"dept_id": inst_id, "year": start_year})
        if okr_predict:
            print(okr_predict.get("okr", 0), "<<---")
            return okr_predict.get("okr", 0)
        return 0

    def _completion_rate():
        rate = col_top11.find({"dept_id": inst_id, "year": start_year}).distinct("ration")
        if not rate:
            return 0
        return sum(rate)

    strategic_scientists, leading_talents, young, outstanding_engineers = count_title()
    professional_titles_count = count_professional_titles()  # 正高级职称人才数量
    # per_capita_participation_activities:: 机构年度参与活动人次（人才id不去重）/机构年度参与活动的人数（人才id去重）
    # html repr: 人次：实际455次，年初预期6000人次，完成率7.58%。
    # predict = 年初预期
    # completion_rate

    body = {
        "visits": {"total": contact_len, "predict": _inst_okr(), "completion_rate": round(_completion_rate(), 5)},
        "number_of_people": {"total": person_len,
                             "per_capita_participation_activities": round((contact_len / person_len), 3) if person_len else 0.0,
                             "overseas_talents": oversea_talent()},
        "area_distribute": strategic_talent(),
        "national_strategic_talents": {
            "national_strategic_talents": strategic_scientists,
            "leading_talent": leading_talents,
            "young_scientific_technological_talents": young,
            "outstanding_engineer": outstanding_engineers,
        },
        "senior_professional_title": {"total": int(professional_titles_count),
                                      "rate": professional_titles_count / person_len if person_len else 0.0},
    }
    print(body)
    return body


def person_rate(start_year: int = None, end_year: int = None, inst_id=None):
    """
    激活率：15.7%（有2501人在之前三年没有参加过该机构组织的活动）
    留存率： 88%（去年活动联系人才12013人中有10571人今年再次参与活动）
    :return
        retention,                 留存率
        last_year_connect_talent,  前一年参与人数
        this_year_attend_talent,   当前年参与人数
        union_attend_talent,       前一年、当前年都参与的人数
        activation,                激活率
        not_part_org_in_three_years 只在当前年参与的人数
    """

    query = {"end_time": query_year(start_year, end_year)}
    if inst_id:
        query.update(institution_query(inst_id))

    activity_ids_one_year = col_activity.find(query).distinct("_id")
    person_ids_one_year = col_register.find({"activity_id": {"$in": activity_ids_one_year}}).distinct("person_id")

    retention = 0.0
    activation = 0.0
    query = {"end_time": query_year(start_year - 1)}
    query.update(institution_query(inst_id))
    activity_ids_last_year = col_activity.find(query).distinct("_id")
    person_ids_last_year = col_register.find({"activity_id": {"$in": activity_ids_last_year}}).distinct("_id")
    # 计算留存率
    last_year_connect_talent = len(person_ids_last_year)
    this_year_attend_talent = len(person_ids_one_year)
    union_attend_talent = len(set(person_ids_one_year) & set(person_ids_last_year))
    if person_ids_last_year:
        retention = union_attend_talent / last_year_connect_talent
    else:
        retention = 0
    # 去年 前年 大年前
    query = {"end_time": query_year(start_year - 3, start_year - 1)}
    query.update(institution_query(inst_id))
    activity_ids_last_three_year = col_activity.find(query).distinct("_id")
    person_ids_last_three_year = col_activity.find({"activity_id": {"$in": activity_ids_last_three_year}}).distinct(
        "_id"
    )
    # 激活率
    not_part_org_in_three_years = 0
    if person_ids_one_year:
        not_part_org_in_three_years = len(set(person_ids_one_year) - set(person_ids_last_three_year))
        activation = not_part_org_in_three_years / len(person_ids_one_year)
    return (
        retention,
        last_year_connect_talent,
        this_year_attend_talent,
        union_attend_talent,
        activation,
        not_part_org_in_three_years,
    )


def task_person_reputation(person_id: ObjectId):
    """
    身份值identity_value
    """
    person_doc = col_person.find_one({"_id": person_id}, {"jobs_about_cast": 1, "term_number_of_jobs_about_cast": 1})
    jobs_about_cast = person_doc.get("jobs_about_cast", [])
    identity_score = 0
    if jobs_about_cast:
        term_number_of_jobs_about_cast = person_doc.get("term_number_of_jobs_about_cast") or []

        jobs = zip(jobs_about_cast, term_number_of_jobs_about_cast)
        for job in jobs:
            cast_score = identity_values.get(job[0])
            if cast_score:
                term = re.split(r"，|,|\s+", job[1])
                identity_score += cast_score * len(term)

    return identity_score


def task_update_inst_act_agg(inst_id: str, year: int = None):
    """
    机构的 组织活动统计
    定时周期任务
    """
    year = year or datetime.utcnow().year
    regular, key, major, act_counts = act_importance_of_activities(inst_id=inst_id, start_year=year)
    regions, provinces = act_region(inst_id=inst_id, start_year=year)

    months_set, quarter_set = act_datetime(inst_id=inst_id, start_year=year)

    single_ids, double_ids, hk_ids, internation_ids = international_tag(inst_id=inst_id, start_year=year)

    update_body = {
        "year": year,
        "organize_activity": act_counts,
        "activity_importance": [
            {"name": "major_events", "value": major},
            {"name": "key_events", "value": key},
            {"name": "regular_events", "value": regular},
        ],
        "host_area": regions,  # 辐射0大地区，0个省级行政区
        "provincial_administrative_region": provinces,
        "host_time": {"quarter": quarter_set, "month": months_set},
        "degree_of_internationalization": {
            "total": internation_ids,
            "multilatera": double_ids,
            "bilateral": single_ids,
            "hk_tw": hk_ids,
        },
    }
    # 存在更新

    inst_db = col_institution.update_one(
        {"_id": ObjectId(inst_id), "m_activity_overview.year": year},
        {"$set": {"m_activity_overview.$": update_body}},
    )
    # 插入
    if inst_db.matched_count < 1:
        col_institution.update_one(
            {"_id": ObjectId(inst_id), "m_activity_overview.year": {"$ne": year}},
            {"$push": {"m_activity_overview": update_body}},
        )


def task_update_inst_alumni_agg(inst_id: str, year: int = None):
    """
    活动联系人才
    周期任务
    """
    year = year or datetime.utcnow().year
    (
        retention,
        last_year_connect_talent,
        this_year_attend_talent,
        union_attend_talent,
        activation,
        not_part_org_in_three_years,
    ) = person_rate(inst_id=inst_id, start_year=year)

    overview_dict = person_overview(inst_id=inst_id,
                                    start_year=year)

    update_body = {
        "year": year,
        **overview_dict,
        # 激活率信息，total 激活率
        "activation_rate": {"total": int(activation), "not_part_org_in_three_years": not_part_org_in_three_years},
        # 留存率信息
        "retention_rate": {"total": int(retention), "last_year_connect_talent": last_year_connect_talent,
                           "this_year_attend_talent": this_year_attend_talent,
                           "union_attend_talent": union_attend_talent},
    }

    inst_db = col_institution.update_one(
        {"_id": ObjectId(inst_id), "m_contact_overview.year": year},
        {"$set": {"m_contact_overview.$": update_body}},
    )

    # 插入
    if inst_db.matched_count < 1:
        col_institution.update_one(
            {"_id": ObjectId(inst_id), "m_contact_overview.year": {"$ne": year}},
            {"$push": {"m_contact_overview": update_body}},
        )


if __name__ == '__main__':
    for inst_id in col_institution.find({"level": 1}).distinct("_id"):
        task_update_inst_alumni_agg(inst_id=str(inst_id), year=2022)
        task_update_inst_alumni_agg(inst_id=str(inst_id), year=2023)
        task_update_inst_act_agg(inst_id=str(inst_id), year=2022)
        task_update_inst_act_agg(inst_id=str(inst_id), year=2023)
